﻿/************************************************************************

   Extended Silverlight Toolkit

   Copyright (C) 2010-2012 Xceed Software Inc.

   This program is provided to you under the terms of the Microsoft Public
   License (Ms-PL) as published at http://extendedsilverlight.codeplex.com/license

   Please purchase a commercial version of this toolkit if you are using
   it in a commercial product. The commercial versions support the project,
   and also include more features for each control, priority support,
   source code, updates, removed watermark, and a proprietary license.

   Visit http://xceed.com and follow @datagrid on Twitter.

  **********************************************************************/

using System;
using System.Collections;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;

namespace Xceed.Silverlight.DataGrid
{
  public sealed class SelectedItemCollection : IList<object>, IList, INotifyPropertyChanged
  {
    #region Constructor

    internal SelectedItemCollection( DataGridControl dataGridControl, ObservableCollection<SelectionRange> selectedRanges )
    {
      m_dataGridControl = dataGridControl;

      selectedRanges.CollectionChanged += new NotifyCollectionChangedEventHandler( this.OnSelectedRangesChanged );
    }

    #endregion

    private void OnSelectedRangesChanged( object sender, NotifyCollectionChangedEventArgs e )
    {
      this.ClearSelectedItems();

      this.OnPropertyChanged( "Count" );
      this.OnPropertyChanged( "Item[]" );
    }

    private void ClearSelectedItems()
    {
      m_selectedItems.Clear();
      m_selectedItems.TrimExcess();

      m_isDirty = true;
    }

    private void UpdateSelectedItems()
    {
      if( ( m_dataGridControl.DataGridContext.DataSource == null )
        || ( m_dataGridControl.DataGridContext.DataSource.Provider == null )
        || ( m_dataGridControl.IsDataSourceAsynchronous ) )
      {
        this.ClearSelectedItems();
      }
      // Update the list of selected items only when required.
      else if( m_isDirty )
      {
        IAsyncResult asyncResult = m_dataGridControl.BeginGetSelectedItems( null, null );

        if( ( !asyncResult.CompletedSynchronously ) || ( !asyncResult.IsCompleted ) )
          throw new InvalidOperationException( "Unable to retrieve the selected items." );

        this.ClearSelectedItems();

        m_selectedItems.AddRange( m_dataGridControl.EndGetSelectedItems( asyncResult ) );
      }

      m_isDirty = false;
    }

    private void AlterSelectedItems()
    {
      if( ( m_dataGridControl.DataGridContext.DataSource != null )
        && ( m_dataGridControl.DataGridContext.DataSource.Provider != null )
        && ( m_dataGridControl.IsDataSourceAsynchronous ) )
        throw new InvalidOperationException( "The SelectedItemCollection can only be modified by synchronous data sources." );
    }

    #region IList<object> Members

    public object this[ int index ]
    {
      get
      {
        this.UpdateSelectedItems();

        if( ( index < 0 ) || ( index >= m_selectedItems.Count ) )
          throw new ArgumentOutOfRangeException( "index", "index must be greater than or equal to zero and less than Count." );

        return m_selectedItems[ index ];
      }
      set
      {
        throw new NotSupportedException();
      }
    }

    public int IndexOf( object item )
    {
      this.UpdateSelectedItems();

      return m_selectedItems.IndexOf( item );
    }

    public void Insert( int index, object item )
    {
      this.Add( item );
    }

    public void RemoveAt( int index )
    {
      this.UpdateSelectedItems();

      if( ( index < 0 ) || ( index >= m_selectedItems.Count ) )
        throw new ArgumentOutOfRangeException( "index", "index must be greater than or equal to zero and less than Count." );

      this.Remove( m_selectedItems[ index ] );
    }

    #endregion

    #region IList Members

    public bool IsReadOnly
    {
      get
      {
        return false;
      }
    }

    public bool IsFixedSize
    {
      get
      {
        return false;
      }
    }

    int IList.Add( object item )
    {
      this.Add( item );

      this.UpdateSelectedItems();

      return m_selectedItems.Count - 1;
    }

    void IList.Clear()
    {
      this.Clear();
    }

    void IList.Insert( int index, object item )
    {
      this.Insert( index, item );
    }

    void IList.Remove( object item )
    {
      this.Remove( item );
    }

    void IList.RemoveAt( int index )
    {
      this.RemoveAt( index );
    }

    #endregion IList Members

    #region ICollection<object> Members

    public void Add( object item )
    {
      this.AlterSelectedItems();

      // Create a SelectionRange for the target item.
      SelectionRange selectionRange = new SelectionRange( item );
      selectionRange.SelectionType = SelectionType.Selection;

      m_dataGridControl.SelectedRanges.Add( selectionRange );
    }

    public void Clear()
    {
      m_dataGridControl.ClearAllSelections();
    }

    public bool Contains( object item )
    {
      this.UpdateSelectedItems();

      return m_selectedItems.Contains( item );
    }

    public void CopyTo( object[] array, int arrayIndex )
    {
      this.UpdateSelectedItems();

      ICollection<object> source = m_selectedItems;
      source.CopyTo( array, arrayIndex );
    }

    public bool Remove( object item )
    {
      this.AlterSelectedItems();

      // Create a SelectionRange for the target item.
      SelectionRange selectionRange = new SelectionRange( item );
      selectionRange.SelectionType = SelectionType.Unselection;

      m_dataGridControl.SelectedRanges.Add( selectionRange );

      return true;
    }

    #endregion

    #region ICollection Members

    public int Count
    {
      get
      {
        this.UpdateSelectedItems();

        return m_selectedItems.Count;
      }
    }

    bool ICollection.IsSynchronized
    {
      get
      {
        return false;
      }
    }

    object ICollection.SyncRoot
    {
      get
      {
        return m_syncRoot;
      }
    }

    void ICollection.CopyTo( Array array, int arrayIndex )
    {
      this.UpdateSelectedItems();

      ICollection source = m_selectedItems;
      source.CopyTo( array, arrayIndex );
    }

    #endregion

    #region IEnumerable<SelectionRange> Members

    public IEnumerator<object> GetEnumerator()
    {
      this.UpdateSelectedItems();

      return m_selectedItems.GetEnumerator();
    }

    #endregion

    #region IEnumerable Members

    IEnumerator IEnumerable.GetEnumerator()
    {
      return this.GetEnumerator();
    }

    #endregion

    #region INotifyPropertyChanged Members

    public event PropertyChangedEventHandler PropertyChanged;

    private void OnPropertyChanged( string propertyName )
    {
      if( this.PropertyChanged != null )
      {
        this.PropertyChanged( this, new PropertyChangedEventArgs( propertyName ) );
      }
    }

    #endregion

    #region Private Fields

    private DataGridControl m_dataGridControl;
    private List<object> m_selectedItems = new List<object>( 0 );
    private bool m_isDirty = true;
    private object m_syncRoot = new object();

    #endregion
  }
}
